home *** CD-ROM | disk | FTP | other *** search
/ MacFormat UK 55 / MF_UK_55_1.iso / mac / Shareware Plus / Developers / Caveman Sound System / CMSoundSystem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-06-20  |  48.5 KB  |  1,889 lines  |  [TEXT/ALFA]

  1. /*****************************************************************************
  2.  * FILE:        CMSoundSystem.c
  3.  * AUTHOR:        David Hay
  4.  * CREATED:        March 9, 1995
  5.  * DESCRIPTION:    Routines for playing sound and music.
  6.  *
  7.  * Copyright © 1995-1997 David Hay
  8.  *
  9.  * Permission to use, copy, and distribute this software and its documentation
  10.  * for any purpose is hereby granted without fee,  provided that (i) the above
  11.  * copyright notices  and  this permission notice  appear in all copies of the
  12.  * software  and  related documentation,  and (ii) the names of David Hay  and
  13.  * Caveman Creations may not be used in any advertising or publicity  relating
  14.  * to the software without the specific, prior written permission of David Hay
  15.  *
  16.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS,
  17.  * IMPLIED  OR  OTHERWISE,  INCLUDING,  WITHOUT  LIMITATION,  ANY WARRANTY  OF
  18.  * MERCHANTABILITY  OR  FITNESS FOR  A PARTICULAR PURPOSE.  IN NO EVENT  SHALL
  19.  * DAVID HAY  OR  CAVEMAN CREATIONS  BE LIABLE  FOR  ANY SPECIAL,  INCIDENTAL,
  20.  * INDIRECT  OR  CONSEQUENTIAL DAMAGES OF ANY KIND,  OR ANY DAMAGES WHATSOEVER
  21.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS,  WHETHER OR NOT ADVISED OF THE
  22.  * POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
  23.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  24.  *
  25.  *
  26.  * Version History:
  27.  *
  28.  * 1.0 (09/11/95)
  29.  *        - Initial release
  30.  *
  31.  * 1.1 (03/05/96)
  32.  *        - Incorporated changes by Dario Accornero (dappsoft@mix.it)  Thanks
  33.  *          Dario for all your work! (look for "DA on 01/20/96")
  34.  *            • added PPC support in the callback routine
  35.  *            • added stero Macs support in the channel allocation routines
  36.  *            • added double buffer support in order to speed up sound output
  37.  *              on all Macs
  38.  *            • chaned SoundSystem allocation, in order to lower global vars
  39.  *              space memory charge; now, the SoundSystem is accessed thru a
  40.  *              pointer, so it is *necessare* to allocate the system before
  41.  *              using it, otherwise the code will crash…
  42.  *            • added priorities support
  43.  *            • added channel volume control (get/set)
  44.  *            • CodeWarrior is now supported
  45.  *        - Cleaned up the code a bit.
  46.  *        - Added some internal routines for code that was getting repeated by
  47.  *          both sound and music routines.
  48.  * 1.1.1 (06/14/97)
  49.  *        - Fixed a bug in CMSRegisterSound() that resulted in a memory leak and
  50.  *          could possibly cause a random system crash.
  51.  *****************************************************************************/
  52.  
  53.  
  54. #ifndef __GESTALT__
  55. #include <Gestalt.h>
  56. #endif
  57. #ifndef __SOUND__
  58. #include <Sound.h>
  59. #endif
  60. #ifndef __TRAPS__
  61. #include <Traps.h>
  62. #endif
  63. #ifndef __CMSOUNDSYSTEM__
  64. #include "CMSoundSystem.h"
  65. #endif
  66.  
  67.  
  68. #ifndef PUBLIC
  69. #define PUBLIC
  70. #endif
  71. #ifndef PRIVATE
  72. #define PRIVATE static
  73. #endif
  74.  
  75. /* DA on 01/20/96: previously defined as long quantities, but SndCommand.param1
  76. ** is short; this could have caused troubles on the PPC, which is a little endian,
  77. ** whereas the 68K is a big endian.
  78. */
  79. #define kSoundDone            99            /* a sound is done playing */
  80. #define kMusicDone            100            /* The music block is done playing */
  81. #define kSoundResourceDone    101            /* a sound resource is done playing */
  82. #define kDoubleBufferSize     4096        /* size of each double buffer (in bytes) */
  83.  
  84. /*****************************************************************************
  85. ** DBLocal        -- Double buffer local variables             DA on 01/20/96
  86. **
  87. **        header        - information about the sound
  88. **
  89. **        bytesTotal    - total number of samples in the sound sample.
  90. **
  91. **        bytesCopied    - number of samples that have been copied into the
  92. **                      buffer so far.
  93. **
  94. **        dataPtr        - pointer to the data of the sound sample to copy
  95. */
  96. typedef struct DBLocal
  97. {
  98.     SoundHeaderPtr    header;
  99.     long            bytesTotal;
  100.     long            bytesCopied;
  101.     Ptr             dataPtr;
  102.     
  103. } DBLocal, *DBLocalPtr;
  104.  
  105.  
  106. /*****************************************************************************
  107. ** ExtSndChannel    -- Describes an extended sound channel
  108. **
  109. **        channel            - the Sound Manager channel data.  NOTE: this field
  110. **                          MUST be first!  This makes it possible to simply
  111. **                          simply cast the extended sound channel to a sound
  112. **                          channel for passing to Sound Manager routines.
  113. **
  114. **        soundPlaying    - The reference number of the sound currently playing
  115. **                          on this sound channel.
  116. **                                
  117. **        isValid            - Is the sound channel valid?  If not a call to
  118. **                          SndNewChannel() is needed to make the channel valid
  119. **                          again.  Otherwise, the channel has already been
  120. **                          allocated and sound may be played on it.
  121. **
  122. **        soundData        - if the sound playing is an unregistered sound
  123. **                          resource, this is the sound resource playing on the
  124. **                          sound channel.
  125. */
  126. typedef struct ExtSndChannel
  127. {
  128.     SndChannel        channel;
  129.     volatile short    soundPlaying;    /* See CMSWaitForSilence() for the reason */
  130.                                     /* why 'volatile' is required here */
  131.     Boolean            isValid;
  132.     SndListHandle    soundData;
  133.     
  134. } ExtSndChannel, *ExtSndChannelPtr;
  135.  
  136.  
  137. /*****************************************************************************
  138. ** SoundSystem        -- Describes private data the sound system
  139. **
  140. **        numChannels        - the number of sound channels open
  141. **        soundChannel    - The list of available channels.
  142. **
  143. **                        // DA on 01/20/96: double buffers support
  144. **
  145. **        doubleBuffer    - buffers used to play sound data
  146. **        doubleHeader    - necessary headers for the above buffers
  147. **        doubleVars        - double buffers state tracking/data loading areas
  148. **
  149. **                        // DA on 01/20/96: priorities support
  150. **
  151. **        priorities        - table used to track priorities on all channels
  152. **
  153. **        numSounds        - the number of sound that have been registered with
  154. **                          the sound system.
  155. **        soundList        - A list of sound handles that have been registered.
  156. **        soundData        - Every time a sound handle is registered with the
  157. **                          sound system, the sound header is found and placed
  158. **                          in the corresponding slot in this list. This allows
  159. **                          the sound system to play the sounds more efficently
  160. **                          with bufferCmd's rather than using SndPlay().    // DA on 01/20/96
  161. **                                                                        // it was SoundPlay()
  162. **                                                                        // a non-existent call
  163. **        musicSequence    - describes which sounds to play and in what order
  164. **                          for a music sequence
  165. **        musicPosition    - the current position in the music sequence
  166. **
  167. **        smVersion        - the version of the sound manager used.
  168. **        smHasStereo        - is stereo sound is available?
  169. **        smHasStereoMixing
  170. **                        - can the stereo be mixed into a single sound channel?
  171. **        smHasDoubleBuffer
  172. **                        - Are double buffering routines available?
  173. **        smHasMultiChannels
  174. **                        - Are we allowed to have more than one channel open
  175. **                          at a time?
  176. */
  177. typedef struct SoundSystem
  178. {
  179.  
  180.     short                numChannels;
  181.     ExtSndChannelPtr    soundChannel[kMaxChannels];
  182.                                                 
  183.     SndDoubleBufferPtr         doubleBuffer[kMaxChannels * 2];
  184.     SndDoubleBufferHeader    doubleHeader[kMaxChannels];
  185.     DBLocal                    doubleVars[kMaxChannels];
  186.             
  187.     short                    priorities[kMaxChannels];
  188.  
  189.     short            numSounds;
  190.     SndListHandle    soundList[kMaxSounds];
  191.     SoundHeaderPtr    soundData[kMaxSounds];
  192.     
  193.     PlaySequencePtr            musicSequence;
  194.     short                    musicPosition;
  195.     
  196.     NumVersion                smVersion;
  197.     Boolean                    smHasStereo         :1;
  198.     Boolean                    smHasStereoMixing    :1;
  199.     Boolean                    smHasDoubleBuffer    :1;
  200.     Boolean                    smHasMultiChannels    :1;
  201.  
  202. } SoundSystem, *SoundSystemPtr;
  203.  
  204.  
  205. /************************ PRIVATE FUNCTION PROTOTYPES ************************/
  206.  
  207.  
  208. PRIVATE OSErr PlayMusicBlock( short channelNum );
  209.  
  210.  
  211. /* DA on 01/20/96: a sound callback procedure accepts a _pointer_ to a SndCommand,
  212. ** rather than a SndCommand, if it's written in C; previously, it accepted a whole
  213. ** SndCommand, making it impossible to be called on PPC machines.
  214. */
  215. PRIVATE pascal void SoundCallBack( SndChannelPtr theChannel, SndCommand *theCmd );
  216.  
  217.  
  218. /* DA on 01/20/96: local functions used to allocate the SoundSystem, and also to
  219. ** support the use of double buffers.
  220. */
  221. PRIVATE OSErr AllocateSoundSystem(void);
  222. PRIVATE OSErr DBSndPlay(SndChannelPtr channel, short channelNum, SoundHeaderPtr sound);
  223. PRIVATE    pascal void DADoubleBack(SndChannelPtr channel, SndDoubleBufferPtr buffer);
  224.  
  225.  
  226. /* DJH on 03/03/96: Common functions to play sounds used by the sound playing and
  227. ** music routines.
  228. */
  229. PRIVATE OSErr StartSound( SndChannelPtr theChannel,
  230.                           short channelNum,
  231.                           short refNum,
  232.                           short param );
  233. PRIVATE OSErr PrepareChannel( short channelNum );
  234.  
  235.  
  236. /************************** PRIVATE GLOBAL VARIABLES *************************/
  237.  
  238. PRIVATE SndCallBackUPP        soundCallBackUPP;    /* callback when sounds are done */
  239.  
  240. PRIVATE SoundSystemPtr        snd = NULL;             /* sound system state information */
  241.                                                 /* DA on 01/20/96: previously defined */
  242.                                                 /* as a variable rather than a pointer, */
  243.                                                 /* eating up as much as 500+ bytes in */
  244.                                                 /* global vars space… */
  245.  
  246. PRIVATE SndDoubleBackUPP    doubleBackUPP;        /* DA on 01/20/96: double buffer */
  247.                                                 /* callback procedure */
  248.  
  249. /*===========================================================================*/
  250. /*                        PUBLIC FUNCTION DEFINITIONS                        */
  251. /*===========================================================================*/
  252.  
  253.  
  254. /*----------------------- INITIALIZATION/DEALLOCATION -----------------------*/
  255.  
  256.  
  257. PUBLIC OSErr
  258. CMSInitSound( short numChannels )
  259. {
  260.     ExtSndChannelPtr    aChannel;    /* A channel to initialize */
  261.     short                ii;            /* misc counter */
  262.     long                feature;    /* result value from Gestalt() */
  263.     OSErr                err;
  264.  
  265.     /* DA on 01/20/96: allocate the SoundSystem */
  266.  
  267.     if ((err = AllocateSoundSystem()) != noErr)
  268.         return    err;
  269.  
  270.  
  271.  
  272.         /*    Get some information about the current sound manager. First we
  273.         **    find out what version of the sound manager is available. To do
  274.         **    this we first have to check if the _SoundDispatch trap is
  275.         **    available. If so, then we can call SndSoundManagerVersion() to
  276.         **    get the version number. Otherwise, the enhanced sound manager
  277.         **    is not present so set the version number to 1.0
  278.         */
  279.     if ( GetToolTrapAddress(_Unimplemented) != GetToolTrapAddress(_SoundDispatch) )
  280.     {
  281.     
  282.             /* DA on 01/20/96: Universal Interfaces 2.x and later are returning
  283.             ** a long quantity from SndSoundManagerVersion()
  284.             **
  285.             ** DJH on 03/03/96: Huh?  No it doesn't.
  286.             **
  287.             ** DJH on 05/21/96: The Metrowerks compiler includes headers where
  288.             ** SndSoundManagerVersion returns a long, so check for the compiler
  289.             ** and use the appropriate call according to the headers in use.
  290.             **
  291.             ** DA on 07/24/96: Metrowerks CodeWarrior 9 and later headers are
  292.             ** now returning a NumVersion struct. Removed the __MWERKS__ checks
  293.             **
  294.             */
  295. // #ifdef __MWERKS__
  296. //        long v = (long *)&snd->smVersion;
  297. //        *v = SndSoundManagerVersion();
  298. // #else
  299.         snd->smVersion = SndSoundManagerVersion();
  300. // #endif
  301.     }
  302.     else
  303.     {
  304.         snd->smVersion.majorRev = 1;
  305.         snd->smVersion.minorAndBugRev = 0;
  306.         snd->smVersion.stage = finalStage;
  307.         snd->smVersion.nonRelRev = 0;
  308.     }
  309.         
  310.         
  311.         
  312.         /*    Now that we have the version number, check for some capabilities
  313.         **    on the current system using gestalt. To check if double buffering
  314.         **    or multiple channels are available, we check two ways. If Sound
  315.         **    Manager 3.0 is available, we can simply look at the result
  316.         **    returned from Gestalt. Otherwise, we see if the ASC chip is
  317.         **    present to determine if the desired features are available.
  318.         */
  319.     err = Gestalt( gestaltSoundAttr, &feature );
  320.     if ( err == noErr )
  321.     {
  322.         snd->smHasStereo = ((feature & (1L << gestaltStereoCapability)) != 0);
  323.         snd->smHasStereoMixing = ((feature & (1L << gestaltStereoMixing)) != 0);
  324.         if ( snd->smVersion.majorRev >= 3 )
  325.         {
  326.             snd->smHasDoubleBuffer =
  327.                         ((feature & (1L << gestaltSndPlayDoubleBuffer)) != 0);
  328.             snd->smHasMultiChannels =
  329.                         ((feature & (1L << gestaltMultiChannels)) != 0);
  330.         }
  331.         else
  332.         {
  333.             err = Gestalt( gestaltHardwareAttr, &feature );
  334.             if ( err == noErr )
  335.             {
  336.                 snd->smHasDoubleBuffer =
  337.                         ((feature & (1L << gestaltHasASC)) != 0);
  338.                 snd->smHasMultiChannels = snd->smHasDoubleBuffer;
  339.             }
  340.         }
  341.     }
  342.     
  343.     
  344.         /*    Initialize the sound channel information so we know
  345.         **    what to dispose of if an error occurs.
  346.         */
  347.     snd->numChannels = 0;
  348.     for ( ii = 0; ii < kMaxChannels; ii++ )
  349.     {
  350.         snd->soundChannel[ii] = NULL;
  351.         snd->priorities[ii] = 0;
  352.     }
  353.     
  354.  
  355.         /*    Initialize the sound data information to indicate that
  356.         **    no sounds have been registered with the sound system
  357.         */
  358.     snd->numSounds = 0;
  359.     for ( ii = 0; ii < kMaxSounds; ii++ )
  360.     {
  361.         snd->soundData[ii] = NULL;
  362.         snd->soundList[ii] = NULL;
  363.     }
  364.  
  365.  
  366.         /*    Initialize the music information to indicate that no
  367.         **    music has been loaded
  368.         */
  369.     snd->musicPosition = kNoSound;
  370.     snd->musicSequence = NULL;
  371.     
  372.  
  373.         /*    Setup the callback routine pointer for sounds. We will use the
  374.         **    same routine to handle sounds and music by putting a different
  375.         **    number in the callback command to determine what to do.
  376.         */
  377.     soundCallBackUPP = NewSndCallBackProc( SoundCallBack );
  378.  
  379.  
  380.  
  381.         /* DA on 01/20/96: create a routine descriptor for our doublebuffer
  382.         ** callback if the system is supporting double buffers
  383.         */
  384.     if (snd->smHasDoubleBuffer)
  385.         doubleBackUPP = NewSndDoubleBackProc(DADoubleBack);
  386.     else
  387.         doubleBackUPP = nil;
  388.  
  389.  
  390.  
  391.         /*    Allocate the sound channels. If more than one sound channel is
  392.         **    requested and multiple sound channels are not available, then
  393.         **  report the error.
  394.         */
  395.     for ( ii = 0; ii < numChannels; ii++ )
  396.     {
  397.         aChannel = (ExtSndChannelPtr) NewPtr( sizeof( ExtSndChannel ) );
  398.         if ( !aChannel )
  399.         {
  400.             err = MemError();
  401.             break;
  402.         }
  403.         else
  404.         {
  405.             aChannel->channel.qLength = stdQLength;
  406.             aChannel->channel.userInfo = ii;
  407.             aChannel->soundPlaying = kNoSound;
  408.             aChannel->isValid = false;
  409.             aChannel->soundData = NULL;
  410.             snd->soundChannel[ii] = aChannel;
  411.         }
  412.     }
  413.     if ( err == noErr )
  414.     {
  415.         snd->numChannels = numChannels;
  416.     }
  417.     else
  418.     {
  419.         CMSDisposeSound();
  420.     }
  421.         
  422.     return err;
  423. }
  424.  
  425. /*---------------------------------------------------------------------------*/
  426. /* DA on 01/20/96: simplified initialization of the whole package */
  427.  
  428. PUBLIC OSErr
  429. CMSOpenSoundTool( short numChannels )
  430. {
  431.     OSErr    err;
  432.  
  433.         /* safety check on number of channels */
  434.     
  435.     if ( numChannels < 0 )
  436.     {
  437.         numChannels = 1;
  438.     }
  439.     else if ( numChannels > kMaxChannels )
  440.     {
  441.         numChannels = kMaxChannels;
  442.     }
  443.  
  444.  
  445.         /* initialize SoundSystem */
  446.  
  447.     err = CMSInitSound( numChannels );
  448.     
  449.     
  450.         /* open all sound channels requested */
  451.     
  452.     if ( err == noErr )
  453.     {
  454.         err = CMSOpenAllChannels();
  455.     }
  456.         
  457.     return    err;
  458. }
  459.  
  460. /*---------------------------------------------------------------------------*/
  461. /* DA on 01/20/96: allocate space for the sound system */
  462.  
  463. PRIVATE OSErr
  464. AllocateSoundSystem( void )
  465. {
  466.     if ( snd == NULL )
  467.     {
  468.         snd = (SoundSystemPtr) NewPtrClear( sizeof( SoundSystem ) );
  469.         if ( !snd )
  470.         {
  471.             return    MemError();
  472.         }
  473.     }
  474.     
  475.     return    noErr;
  476. }
  477.  
  478. /*---------------------------------------------------------------------------*/
  479.  
  480. PUBLIC void
  481. CMSDisposeSound( void )
  482. {
  483.     short        ii;
  484.  
  485.         /* DA on 01/20/96: if the SoundSystem wasn't allocated, just return;
  486.         ** this is necessary to avoid accessing a null pointer
  487.         */
  488.     if ( snd == NULL ) return;
  489.  
  490.  
  491.         /* Destroy all of the sound channels */
  492.  
  493.     for ( ii = 0; ii < snd->numChannels; ii++ )
  494.     {
  495.         if ( snd->soundChannel[ii] )
  496.         {
  497.             CMSCloseChannel( ii );
  498.             DisposePtr( (Ptr) snd->soundChannel[ii] );
  499.             snd->soundChannel[ii] = NULL;
  500.         }
  501.     }
  502.     
  503.     
  504.     DisposeRoutineDescriptor( soundCallBackUPP );
  505.  
  506.  
  507.         /* DA on 01/20/96: if we were using double buffers, dispose of
  508.         ** the relative routine descriptor
  509.         */
  510.     if ( snd->smHasDoubleBuffer )
  511.     {
  512.         DisposeRoutineDescriptor(doubleBackUPP);
  513.     }
  514.  
  515.     
  516.         /* Dispose of the registered sound data */
  517.  
  518.     for ( ii = 0; ii < snd->numSounds; ii++ )
  519.     {
  520.         if ( snd->soundList[ii] )
  521.         {
  522.             HUnlock( (Handle) snd->soundList[ii] );
  523.             DisposeHandle( (Handle) snd->soundList[ii] );
  524.         }
  525.         snd->soundData[ii] = NULL;
  526.         snd->soundList[ii] = NULL;
  527.     }
  528.     
  529.     
  530.         /* Dispose of the music (if any) */
  531.         
  532.     if ( snd->musicSequence )
  533.     {
  534.         DisposePtr( (Ptr) snd->musicSequence );
  535.         snd->musicSequence = NULL;
  536.     }
  537.     
  538.         /* DA on 01/20/96: dispose of the SoundSystem */
  539.     
  540.     DisposePtr( (Ptr)snd );
  541. }
  542.  
  543.  
  544.  
  545. /*---------------------------- CHANNEL MANAGEMENT ---------------------------*/
  546.  
  547. PUBLIC OSErr
  548. CMSCloseChannel( short channelNum )
  549. {
  550.     OSErr    err;
  551.     
  552.     err = noErr;
  553.     if ( snd->soundChannel[channelNum]->isValid )
  554.     {
  555.         err = SndDisposeChannel( (SndChannelPtr) snd->soundChannel[channelNum],
  556.                                  true );
  557.         
  558.             /* DA on 01/20/96: dispose of the double buffers if the system 
  559.             ** supports them
  560.             */
  561.         if ( snd->smHasDoubleBuffer )
  562.         {
  563.             DisposePtr( (Ptr)snd->doubleBuffer[channelNum * 2] );
  564.             DisposePtr( (Ptr)snd->doubleBuffer[(channelNum * 2) + 1] );
  565.         }
  566.         
  567.         snd->soundChannel[channelNum]->isValid = false;
  568.     }
  569.     
  570.     return err;
  571. }
  572.  
  573. /*---------------------------------------------------------------------------*/
  574.  
  575. PUBLIC OSErr
  576. CMSCloseAllChannels( void )
  577. {
  578.     short    ii;
  579.     OSErr    err;
  580.     
  581.     err = noErr;
  582.     for ( ii = 0; ii < snd->numChannels; ii++ )
  583.     {
  584.         err = CMSCloseChannel( ii );
  585.     }
  586.         
  587.     return err;
  588. }
  589.     
  590. /*---------------------------------------------------------------------------*/
  591.  
  592. PUBLIC OSErr
  593. CMSOpenChannel( short channelNum )
  594. {
  595.     OSErr                err;
  596.     SndDoubleBufferPtr    doubleBuffer;
  597.     
  598.     err = noErr;
  599.     if ( !snd->soundChannel[channelNum]->isValid )
  600.     {
  601.             /* DA on 01/20/96:  create a sound channel; take advantage of
  602.             ** stereo output on systems supporting it
  603.             */
  604.         err = SndNewChannel( (SndChannelPtr*) &snd->soundChannel[channelNum],
  605.                              sampledSynth, initNoInterp + (snd->smHasStereo ? initStereo : initMono),
  606.                              soundCallBackUPP );
  607.                              
  608.         if ( err == noErr )
  609.         {
  610.             snd->soundChannel[channelNum]->isValid = true;
  611.         }
  612.         
  613.         
  614.             /* DA on 01/20/96:  create double buffer information if the
  615.             ** system supports it.
  616.             */
  617.         if (err == noErr)
  618.         {
  619.             if (snd->smHasDoubleBuffer)
  620.             {
  621.                     /* DA on 01/20/96:  create double buffers for specified channel */
  622.             
  623.                 doubleBuffer = (SndDoubleBufferPtr) NewPtr( sizeof( SndDoubleBuffer ) +
  624.                                                             kDoubleBufferSize);
  625.                 if ( !doubleBuffer)
  626.                 {
  627.                     return    MemError();
  628.                 }
  629.                 
  630.                 snd->doubleBuffer[channelNum * 2] = doubleBuffer;
  631.                 snd->doubleHeader[channelNum].dbhBufferPtr[0] = doubleBuffer;
  632.                 
  633.                 doubleBuffer = (SndDoubleBufferPtr) NewPtr( sizeof( SndDoubleBuffer ) +
  634.                                                             kDoubleBufferSize );
  635.                 if ( doubleBuffer == NULL )
  636.                 {
  637.                     DisposePtr( (Ptr)snd->doubleBuffer[channelNum * 2] );
  638.                     return    MemError();
  639.                 }
  640.                 
  641.                 snd->doubleBuffer[(channelNum * 2) + 1] = doubleBuffer;
  642.                 snd->doubleHeader[channelNum].dbhBufferPtr[1] = doubleBuffer;
  643.                 
  644.                 
  645.                     /* DA on 01/20/96
  646.                     ** Setup common information in the double buffer headers
  647.                     ** WARNING: the following lines are assuming that _only_ this
  648.                     ** type of sounds will be used: mono, 8 bits, uncompressed,
  649.                     ** any frequency
  650.                     **
  651.                     ** DJH on 03/03/96:  Took this warning to heart and moved it
  652.                     ** to DBSndPlay where we take other kindes of sounds into
  653.                     ** account. (e.g. 16-bit, compressed, etc.)
  654.                     */
  655.                 
  656.                 
  657.                     /* DA on 01/20/96 assign doubleback procedure to the
  658.                     ** double buffer
  659.                     */
  660.                 snd->doubleHeader[channelNum].dbhDoubleBack = doubleBackUPP;
  661.                 
  662.             }
  663.         }
  664.     }
  665.     
  666.     return err;
  667. }
  668.  
  669. /*---------------------------------------------------------------------------*/
  670.  
  671. PUBLIC OSErr
  672. CMSOpenAllChannels( void )
  673. {
  674.     short    ii;
  675.     OSErr    err;
  676.     
  677.     err = noErr;
  678.     for ( ii = 0; ii < snd->numChannels; ii++ )
  679.         err = CMSOpenChannel( ii );
  680.         
  681.     return err;
  682. }
  683.  
  684. /*---------------------------------------------------------------------------*/
  685.  
  686. PUBLIC OSErr
  687. CMSStopSound( short channelNum )
  688. {
  689.     SndCommand            theCommand;
  690.     ExtSndChannelPtr    sndChannel;
  691.     OSErr                err;
  692.     
  693.     sndChannel = snd->soundChannel[channelNum];
  694.  
  695.         /* Flush the sound channel of any other sound commands */
  696.         
  697.     theCommand.cmd = flushCmd;
  698.     theCommand.param1 = 0;
  699.     theCommand.param2 = 0L;
  700.     err = SndDoImmediate( (SndChannelPtr) sndChannel, &theCommand );
  701.     
  702.     
  703.         /* Send a quiet command to stop any currently playing sounds */
  704.  
  705.     if ( err == noErr )
  706.     {
  707.         theCommand.cmd = quietCmd;
  708.         theCommand.param1 = 0;
  709.         theCommand.param2 = 0L;
  710.         err = SndDoImmediate( (SndChannelPtr) sndChannel, &theCommand );
  711.     }
  712.     
  713.     sndChannel = snd->soundChannel[channelNum];
  714.     sndChannel->soundPlaying = kNoSound;
  715.     if ( sndChannel->soundData )
  716.     {
  717.         HUnlock( (Handle) sndChannel->soundData );
  718.         sndChannel->soundData = NULL;
  719.     }    
  720.  
  721.     return err;
  722. }
  723.  
  724. /*---------------------------------------------------------------------------*/
  725.  
  726. PUBLIC void
  727. CMSIdleSoundTask( void )
  728. {
  729.     short                ii;
  730.     ExtSndChannelPtr    sndChannel;
  731.     
  732.         /*    Loop over the available channels checking to see if one has
  733.         **    fallen silent. If so and there is a sound resource attached
  734.         **    to the channel, then unlock the sound so that it may be
  735.         **    purged. Also detach the sound resource from the channel so
  736.         */
  737.     for ( ii = 0; ii < snd->numChannels; ii++ )
  738.     {
  739.         sndChannel = snd->soundChannel[ii];
  740.         if ( sndChannel->soundPlaying == kNoSound && sndChannel->soundData )
  741.         {
  742.             HUnlock( (Handle) sndChannel->soundData );
  743.             sndChannel->soundData = NULL;
  744.         }
  745.     }
  746. }
  747.  
  748. /*---------------------------------------------------------------------------*/
  749.  
  750. PUBLIC OSErr
  751. CMSGetSoundPlaying( short channelNum, short* refNum )
  752. {
  753.     if ( channelNum >= 0 && channelNum < snd->numChannels )
  754.     {
  755.         *refNum = snd->soundChannel[channelNum]->soundPlaying;
  756.         return noErr;
  757.     }
  758.     
  759.     return badChannel;
  760. }
  761.  
  762. /*---------------------------------------------------------------------------*/
  763.  
  764. PUBLIC OSErr
  765. CMSWaitForSilence( short channelNum )
  766. {
  767.     if ( channelNum >= 0 && channelNum < snd->numChannels )
  768.     {
  769.             /*    We turn off global optimizer to keep the compiler from putting
  770.             **    putting our loop control variable into a register. Since we
  771.             **    are waiting for an interrupt to occur, putting the LCV in a
  772.             **    register would put us in an infinite loop.  By disabling the
  773.             **  compiler's ability to assign variables to registers, we are
  774.             **    able to avoid this nasty problem.  I don't know if this hack
  775.             **    will work on the PPC since I don't have a PPC compiler.
  776.             */
  777.  
  778.             /* DJH on 07/03/96:  The #pragma has been removed since I was
  779.             ** reminded that making the 'soundPlaying' variable 'volatile'
  780.             ** tells the compiler to avoid aggressive optimiation on that
  781.             ** object.  Note that since this is only a suggestion,  I would
  782.             ** recommend that you check the assembly generated by your
  783.             ** to make sure that this value is not stuffed into a register
  784.             ** before the loop is entered.  Think C 5.0 does it right, but
  785.             ** I don't know about other compilers.
  786.             */
  787.  
  788. //#ifdef THINK_C
  789. //# pragma options( !global_optimizer )
  790. //#endif
  791.  
  792.         while( snd->soundChannel[channelNum]->soundPlaying != kNoSound )
  793.         {}    /* Do nothing, just wait for the sound to complete */
  794.  
  795.         return noErr;
  796.     }
  797.     
  798.     return badChannel;    /* invalid channel specified */
  799. }
  800.  
  801.  
  802.  
  803. /*-------------------------- SOUND DATA MANAGEMENT --------------------------*/
  804.  
  805.  
  806. PUBLIC OSErr
  807. CMSRegisterSound( SndListHandle sndHandle, short* refNum )
  808. {
  809.     short            ii;
  810.     SoundHeaderPtr    sndHeader;
  811.     OSErr            err;
  812.     
  813.     err = noErr;
  814.     *refNum = kNoSound;
  815.     if ( sndHandle != NULL )
  816.     {
  817.             /*    First we get the sound header from the sound handle so
  818.             **    that the sound manager does not have to parse the sound
  819.             **    resource each time the sound is played.  The handle is
  820.             **    locked by CMSGetSoundHeader(), so we don't have to worry
  821.             **    about locking it ourselves.
  822.             */
  823.         sndHeader = CMSGetSoundHeader( sndHandle );
  824.         
  825.             /*    Now we need to find a free slot in the list of available
  826.             **    sounds. Once we find a slot, insert the sound data into
  827.             **    that slot and return the slot number as the sound
  828.             **    reference.
  829.             */
  830.         for ( ii = 0; ii < snd->numSounds; ii++ )
  831.         {
  832.             if ( snd->soundData[ii] == NULL )
  833.             {
  834.                 *refNum = ii;
  835.                 snd->soundData[ii] = sndHeader;
  836.                 snd->soundList[ii] = sndHandle;
  837.                 break;
  838.             }
  839.         }
  840.         
  841.             /*    There aren't enough slots, so bump the number of slots
  842.             **    if possible and insert the sound at the end of the sound
  843.             **    list. If there simply isn't any more room, return an error.
  844.             **
  845.             **    1.1.1: DJH on 06/14/97: Don't forget to update sndList here
  846.             **    as well (we did it right in the above case)
  847.             **
  848.             **    1.1.1: DJH on 06/20/97: Now if we find an open slot in the
  849.             **    loop above, we won't return an error.
  850.             */
  851.         if ( *refNum == kNoSound )
  852.         {
  853.             if ( snd->numSounds < kMaxSounds )
  854.             {
  855.                 *refNum = snd->numSounds;
  856.                 snd->soundData[snd->numSounds] = sndHeader;
  857.                 snd->soundList[snd->numSounds] = sndHandle;    /* 1.1.1: DJH */
  858.                 snd->numSounds++;
  859.             }
  860.             else
  861.             {
  862.                 err = notEnoughBufferSpace;
  863.             }
  864.         }
  865.     }
  866.     
  867.     return err;
  868. }
  869. /*---------------------------------------------------------------------------*/
  870.  
  871. PUBLIC OSErr
  872. CMSRemoveSound( short refNum )
  873. {
  874.     short        ii;
  875.     OSErr        err;
  876.     
  877.     err = noErr;
  878.     if ( refNum >= 0 && refNum < snd->numSounds )
  879.     {
  880.             /*    Stop the sound if it is playing on one
  881.             **    of the sound channels
  882.             */
  883.         for ( ii = 0; ii < snd->numChannels; ii++ )
  884.         {
  885.             if ( snd->soundChannel[ii]->soundPlaying == refNum )
  886.             {
  887.                 err = CMSStopSound( ii );
  888.             }
  889.             if ( err != noErr ) break;
  890.         }
  891.         
  892.             /*    Dispose of the sound handle and mark it's slot in the
  893.             **    list of registered sounds as now available.
  894.             **
  895.             **    1.1.1: DJH on 06/20/97: Why don't we use refNum to index into
  896.             **    soundData and soundList rather than ii?
  897.             */
  898.         if ( err == noErr )
  899.         {        
  900.             HUnlock( (Handle) snd->soundList[refNum] );
  901.             DisposeHandle( (Handle) snd->soundList[refNum] );
  902.             snd->soundData[refNum] = NULL;
  903.             snd->soundList[refNum] = NULL;
  904.         }
  905.     }
  906.     
  907.     return err;
  908. }
  909.  
  910. /*---------------------------------------------------------------------------*/
  911.  
  912. PUBLIC OSErr
  913. CMSLoadSound( short soundID, short* refNum )
  914. {
  915.     Handle    sndHandle;
  916.     OSErr    err;
  917.     
  918.     err = noErr;
  919.     
  920.         /* Get the sound resource and detach it from the resource file. */
  921.         
  922.     sndHandle = GetResource( 'snd ', soundID );
  923.     if ( sndHandle == NULL )
  924.         err = ResError();
  925.     
  926.     if ( err == noErr )
  927.     {
  928.         DetachResource( (Handle) sndHandle );
  929.         err = ResError();
  930.     }
  931.     
  932.         /*    Now that we have the sound handle, register the sound
  933.         **    with the sound system.
  934.         */
  935.     if ( err == noErr )
  936.         err = CMSRegisterSound( (SndListHandle) sndHandle, refNum );
  937.         
  938.     return err;
  939. }
  940.     
  941. /*---------------------------------------------------------------------------*/
  942.  
  943. PUBLIC OSErr
  944. CMSPlaySound( short refNum, short channelNum )
  945. {
  946.     OSErr                err;
  947.     
  948.     err = noErr;
  949.     if ( channelNum >= 0 && channelNum < snd->numChannels &&
  950.          refNum >= 0 && refNum < snd->numSounds )
  951.     {
  952.             /* DJH on 03/03/96: Moved the code that was here into a function
  953.             ** since CMSPlayMusic() does the same thing.
  954.             */
  955.         err = PrepareChannel( channelNum );
  956.         
  957.         
  958.             /* Play the sound and install a completion callback routine */
  959.  
  960.         if ( err == noErr )
  961.         {
  962.                 /* DJH on 03/03/96: Moved the code that was here into a
  963.                 ** function since PlayMusicBlock() does nearly the same
  964.                 ** thing.
  965.                 */
  966.             err = StartSound( (SndChannelPtr) snd->soundChannel[channelNum],
  967.                               channelNum, refNum, kSoundDone );
  968.         }
  969.     }
  970.  
  971.     return err;
  972. }
  973.  
  974. /*---------------------------------------------------------------------------*/
  975. /* DJH on 03/03/96: Make sure a channel is ready to play a sound */
  976.  
  977. PRIVATE OSErr
  978. PrepareChannel( short channelNum )
  979. {
  980.     OSErr err;
  981.     
  982.     
  983.         /*    Open the sound channel if it is not already. Otherwise, stop
  984.         **    any sound that may be playing on that sound channel.
  985.         */
  986.     if ( !snd->soundChannel[channelNum]->isValid )
  987.     {
  988.         err = CMSOpenChannel( channelNum );
  989.     }
  990.     else
  991.     {
  992.         err = CMSStopSound( channelNum );
  993.     }
  994.     
  995.     return err;
  996. }
  997.  
  998. /*---------------------------------------------------------------------------*/
  999. /* DJH on 03/03/96: Start a sound going on a channel */
  1000.  
  1001. PRIVATE OSErr
  1002. StartSound( SndChannelPtr theChannel, short channelNum, short refNum, short param )
  1003. {
  1004.     SndCommand theCommand;
  1005.     OSErr err;
  1006.     
  1007.         /* DA on 01/20/96:  if the system is supporting double buffers, take
  1008.         ** advantage of them this results in much faster sound output on all Macs
  1009.         */
  1010.     if ( snd->smHasDoubleBuffer )
  1011.     {
  1012.         err = DBSndPlay( theChannel, channelNum, snd->soundData[refNum] );
  1013.     }
  1014.     else    /* just do a simple buffer command */
  1015.     {
  1016.         theCommand.cmd = bufferCmd;
  1017.         theCommand.param1 = 0;
  1018.         theCommand.param2 = (long) snd->soundData[refNum];
  1019.         err = SndDoImmediate( (SndChannelPtr) theChannel, &theCommand );
  1020.     }
  1021.  
  1022.  
  1023.         /* attach a callback so we know when the sound is done. */
  1024.         
  1025.     if ( err == noErr )
  1026.     {
  1027.         theCommand.cmd = callBackCmd;
  1028.         theCommand.param1 = param;
  1029.         theCommand.param2 = SetCurrentA5();
  1030.         err = SndDoCommand( (SndChannelPtr) theChannel, &theCommand, true );
  1031.     }
  1032.     
  1033.     if ( err == noErr )
  1034.     {
  1035.         ((ExtSndChannelPtr)theChannel)->soundPlaying = refNum;
  1036.         snd->priorities[channelNum] = 0;
  1037.     }
  1038.     
  1039.     return err;
  1040. }
  1041.  
  1042. /*---------------------------------------------------------------------------*/
  1043.  
  1044. PUBLIC OSErr
  1045. CMSPlaySoundResource( short resID, short channelNum, Boolean async )
  1046. {
  1047.     Handle        sndHandle;
  1048.     SndCommand    theCommand;
  1049.     OSErr        err;
  1050.     
  1051.     err = noErr;
  1052.     
  1053.     if ( channelNum >= 0 && channelNum < snd->numChannels )
  1054.     {
  1055.             /*    Open the sound channel if it is not already. Otherwise, stop
  1056.             **    any sound that may be playing on that sound channel.
  1057.             */
  1058.         if ( !snd->soundChannel[channelNum]->isValid )
  1059.         {
  1060.             err = CMSOpenChannel( channelNum );
  1061.         }
  1062.         else
  1063.         {
  1064.             err = CMSStopSound( channelNum );
  1065.         }
  1066.     
  1067.             /*    Get the sound resource. Then lock it down and make it purgeable
  1068.             **    so that it can be freed after the sound is finished playing.
  1069.             */
  1070.         sndHandle = GetResource( 'snd ', resID );
  1071.         if ( sndHandle == NULL )
  1072.             return ResError();
  1073.             
  1074.         HLockHi( sndHandle );
  1075.         HPurge( sndHandle );
  1076.         
  1077.             /*    Play the sound resource and install a callback routine to
  1078.             **    handle the completion of the sound
  1079.             */
  1080.         err = SndPlay( (SndChannelPtr) snd->soundChannel[channelNum],
  1081.                        (SndListHandle) sndHandle, async );
  1082.         if ( err == noErr )
  1083.         {
  1084.             theCommand.cmd = callBackCmd;
  1085.             theCommand.param1 = kSoundResourceDone;
  1086.             theCommand.param2 = SetCurrentA5();
  1087.             err = SndDoCommand( (SndChannelPtr) snd->soundChannel[channelNum],
  1088.                                 &theCommand, true );
  1089.             
  1090.             snd->soundChannel[channelNum]->soundData = (SndListHandle) sndHandle;
  1091.             snd->soundChannel[channelNum]->soundPlaying = kSoundResource;
  1092.         }
  1093.     }    
  1094.     return err;
  1095. }
  1096.  
  1097.  
  1098.  
  1099. /*----------------------------- MUSIC MANAGEMENT ----------------------------*/
  1100.  
  1101.  
  1102. PUBLIC OSErr
  1103. CMSLoadMusic( short musicID )
  1104. {
  1105.     PlaySequenceHandle    sequenceH;            /* Play sequence resource handle */
  1106.     PlaySequencePtr        sequencePtr;        /* for derefing the resource */
  1107.     Size                sequenceSize;        /* number of entries in the sequence */
  1108.     short                ii, jj;                /* misc counters */
  1109.     short                soundLoaded[kMaxSounds]; /* ref nums of loaded sounds */
  1110.     short                soundRef;            /* current sound ref num */
  1111.     short                firstSound;            /* resource # of first sound */
  1112.     short                lastSound;            /* resource # of last sound */
  1113.     OSErr                err;                /* error code */
  1114.     
  1115.         /*    First we get the music list resource and copy it into
  1116.         **    a newly allocated block of memory
  1117.         */
  1118.     sequenceH = (PlaySequenceHandle) GetResource( kMusicResType, musicID );
  1119.     if ( !sequenceH ) return ResError();
  1120.  
  1121.     sequenceSize = GetHandleSize( (Handle) sequenceH );
  1122.     err = MemError();
  1123.     
  1124.     if ( err == noErr )
  1125.     {
  1126.         sequencePtr = (PlaySequencePtr) NewPtr( sequenceSize );
  1127.         if ( sequencePtr == NULL )
  1128.             err = MemError();
  1129.     }
  1130.     
  1131.     if ( err == noErr && sequencePtr )
  1132.     {
  1133.         HLock( (Handle) sequenceH );
  1134.         BlockMove( (Ptr)(*sequenceH),(Ptr)(sequencePtr), sequenceSize );
  1135.         HUnlock( (Handle) sequenceH );
  1136.     }
  1137.     
  1138.     ReleaseResource( (Handle) sequenceH );
  1139.         
  1140.     if ( err == noErr )
  1141.     {
  1142.             /*    Figure out the range of sound resources we are dealing with so
  1143.             **    that we know which sounds to load in. It is assumed that the
  1144.             **    sounds in the music sequence are contiguous and no other sounds
  1145.             **    are involved.
  1146.             */
  1147.         firstSound = sequencePtr->sequence[0];
  1148.         lastSound = sequencePtr->sequence[0];
  1149.         for ( ii = 1; ii < sequencePtr->sequenceLength; ii++ )
  1150.         {
  1151.             if ( sequencePtr->sequence[ii] < firstSound )
  1152.                 firstSound = sequencePtr->sequence[ii];
  1153.                 
  1154.             if ( sequencePtr->sequence[ii] > lastSound )
  1155.                 lastSound = sequencePtr->sequence[ii];
  1156.         }
  1157.     
  1158.             /*    Load the sound data for the music. The sequence resource lists
  1159.             **    the 'snd ' resources to play, so load each one in and update
  1160.             **    the music sequence to refer to the new sound, rather than the
  1161.             **    resource number.
  1162.             */    
  1163.         for ( ii = firstSound; ii <= lastSound; ii++ )
  1164.         {
  1165.             err = CMSLoadSound( ii, &soundRef );
  1166.             if ( err != noErr ) break;
  1167.             
  1168.             soundLoaded[ii - firstSound] = soundRef;
  1169.             for ( jj = 0; jj < sequencePtr->sequenceLength; jj++ )
  1170.             {
  1171.                 if ( sequencePtr->sequence[jj] == ii )
  1172.                     sequencePtr->sequence[jj] = soundRef;
  1173.             }
  1174.         }
  1175.     }
  1176.     
  1177.     
  1178.         /* Cleanup if there were any errors */
  1179.  
  1180.     if ( err == noErr )
  1181.     {
  1182.         sequencePtr->loopStart--;
  1183.         snd->musicSequence = sequencePtr;
  1184.     }
  1185.     else if ( sequencePtr )
  1186.     {
  1187.             /* Dispose of the sounds we could load before the error occured. */
  1188.             
  1189.         ii -= firstSound;
  1190.         while ( --ii >= 0 )
  1191.             CMSRemoveSound( soundLoaded[ii] );
  1192.  
  1193.         DisposePtr( (Ptr) sequencePtr );
  1194.         snd->musicSequence = NULL;
  1195.     }            
  1196.     
  1197.     return err;
  1198. }
  1199.  
  1200.  
  1201. /*---------------------------------------------------------------------------*/
  1202.  
  1203. PUBLIC OSErr
  1204. CMSRemoveMusic( void )
  1205. {
  1206.     PlaySequencePtr    sequencePtr;
  1207.     short            ii;
  1208.     OSErr            err;
  1209.     
  1210.     err = noErr;
  1211.     sequencePtr = snd->musicSequence;
  1212.     if ( sequencePtr )
  1213.     {
  1214.             /*    Loop through the entire play sequence and release each
  1215.             **    sound. If the sound has already been released, skip it
  1216.             **    and move on to the next one.
  1217.             */
  1218.         for ( ii = 0; ii < sequencePtr->sequenceLength; ii++ )
  1219.         {
  1220.             if ( snd->soundData[sequencePtr->sequence[ii]] )
  1221.                 CMSRemoveSound( sequencePtr->sequence[ii] );
  1222.         }
  1223.         
  1224.         DisposePtr( (Ptr) sequencePtr );
  1225.         snd->musicSequence = NULL;
  1226.         snd->musicPosition = 0;
  1227.     }
  1228.     return err;
  1229. }
  1230.     
  1231. /*---------------------------------------------------------------------------*/
  1232.  
  1233. PUBLIC OSErr
  1234. CMSPlayMusic( short channelNum )
  1235. {
  1236.     OSErr    err;
  1237.     
  1238.     err = noErr;
  1239.     if ( snd->musicSequence != NULL )
  1240.     {
  1241.             /* DJH on 03/03/96:  Use the common routine that CMSPlaySound uses */
  1242.         
  1243.         err = PrepareChannel( channelNum );
  1244.  
  1245.             /*    Reset the music position and then play the first
  1246.             **    block of music.
  1247.             */
  1248.         if ( err == noErr )
  1249.         {
  1250.             snd->musicPosition = 0;
  1251.             err = PlayMusicBlock( channelNum );
  1252.         }
  1253.     }
  1254.  
  1255.     return err;
  1256. }
  1257.  
  1258.  
  1259.  
  1260. /*----------------------------- UTILITY ROUTINES ----------------------------*/
  1261.  
  1262.  
  1263. PUBLIC SoundHeaderPtr
  1264. CMSGetSoundHeader( SndListHandle sndHandle )
  1265. {
  1266.     long    offset;        /* offset to sound header */
  1267.     OSErr    err;
  1268.  
  1269.     HLockHi( (Handle) sndHandle );
  1270.     
  1271.         /*    compute offset to sound header and use that offset to
  1272.         **    return a pointer into the sound handle.
  1273.         */
  1274.     err = CMSGetSoundHeaderOffset( sndHandle, &offset );
  1275.     if ( err != noErr )                /* no sound header in resource */
  1276.         return NULL;
  1277.     else                            /* compute address of sound header */
  1278.         return ((SoundHeaderPtr)((Ptr)(*sndHandle) + offset));
  1279. }
  1280.  
  1281. /*---------------------------------------------------------------------------*/
  1282.  
  1283. PUBLIC OSErr
  1284. CMSGetSoundHeaderOffset( SndListHandle soundHandle, long* theOffset )
  1285. {
  1286.     Ptr        sndPtr;            /* to navigate resource    */
  1287.     long    offset;            /* offset into resource    */
  1288.     short    numSynths;        /* info about resource    */
  1289.     short    numCmds;        /* info about resource    */
  1290.     Boolean    isDone;            /* are we done yet?        */
  1291.     OSErr    err;
  1292.  
  1293.         /*    If we have Sound Manager 3.0 or greater, then let it do
  1294.         **    the work for us. Otherwise, we have to parse the sound
  1295.         **    resource ourselves.
  1296.         */
  1297.     if ( snd->smVersion.majorRev >= 3 )
  1298.         return GetSoundHeaderOffset( soundHandle, theOffset );
  1299.  
  1300.         /*    Initialize variables. */
  1301.         
  1302.     offset = 0L;                    /* return 0 if no sound header found */
  1303.     sndPtr = (Ptr)*soundHandle;        /* point to start of resource data */
  1304.     isDone = false;                    /* haven't yet found sound header */
  1305.     err = noErr;
  1306.  
  1307.         /* Skip everything before sound commands. */
  1308.  
  1309.     switch ( ((SndListPtr) sndPtr)->format )
  1310.     {
  1311.         case firstSoundFormat:                /* format 1 'snd ' resource */
  1312.             numSynths = ((SndListPtr) sndPtr)->numModifiers;
  1313.             sndPtr += 2 * sizeof( short ) + numSynths * sizeof( ModRef );
  1314.             break;
  1315.  
  1316.         secondSoundFormat:                    /* format 2 'snd ' resource */
  1317.             sndPtr += 2 * sizeof( short );
  1318.             break;
  1319.             
  1320.         default:                            /* unrecognized resource format */
  1321.             err = badFormat;
  1322.             isDone = true;
  1323.             break;
  1324.     }
  1325.  
  1326.         /* Find number of commands and move to start of first command. */
  1327.         
  1328.     numCmds = *(short*)sndPtr;
  1329.     sndPtr += sizeof( short );
  1330.  
  1331.  
  1332.         /* Search for bufferCmd or soundCmd to obtain sound header. */
  1333.         
  1334.     while ( numCmds >= 1 && !isDone )
  1335.     {
  1336.         if ( (*(short*)sndPtr) == bufferCmd + dataOffsetFlag  ||
  1337.              (*(short*)sndPtr) == soundCmd + dataOffsetFlag )
  1338.         {
  1339.                 /*    bufferCmd or soundCmd found, copy offset
  1340.                 **    from sound command
  1341.                 */
  1342.             offset = ((SndCommand*)sndPtr)->param2;
  1343.             isDone = true;                        /* get out of loop */
  1344.         }
  1345.         else
  1346.         {
  1347.                 /*    soundCmd or bufferCmd not found move to next command
  1348.                 */
  1349.             sndPtr += sizeof( SndCommand );
  1350.             --numCmds;
  1351.         }
  1352.     }
  1353.  
  1354.     *theOffset = offset;        /* return offset */
  1355.     return err;                    /* return result code */
  1356. }
  1357.  
  1358.  
  1359.  
  1360. /*----------------------------- INQUIRY ROUTINES ----------------------------*/
  1361.  
  1362.  
  1363. PUBLIC Boolean
  1364. CMSHasNewSoundManager( void )
  1365. {
  1366.     NumVersion    sndVersion;
  1367.     
  1368.     if ( GetToolTrapAddress(_Unimplemented) != GetToolTrapAddress(_SoundDispatch) )
  1369.     {
  1370. // #ifdef __MWERKS__
  1371. //        long* v = (long*)&sndVersion;
  1372. //        *v = SndSoundManagerVersion();
  1373. // #else
  1374.         sndVersion = SndSoundManagerVersion();
  1375. // #endif
  1376.         
  1377.         return (sndVersion.majorRev >= 2);
  1378.     }
  1379.     
  1380.     return false;
  1381. }
  1382.  
  1383. /*---------------------------------------------------------------------------*/
  1384.  
  1385. PUBLIC Boolean
  1386. CMSHasMultipleChannels( void )
  1387. {
  1388.     OSErr        err;
  1389.     NumVersion    numv;
  1390.     long        feature;
  1391.     
  1392.     err = Gestalt( gestaltSoundAttr, &feature );
  1393.     if ( err == noErr )
  1394.     {
  1395. // #ifdef __MWERKS__
  1396. //        long* v = (long*)&numv;
  1397. //        *v = SndSoundManagerVersion();
  1398. // #else
  1399.         numv = SndSoundManagerVersion();
  1400. // #endif
  1401.         if ( CMSHasNewSoundManager() && numv.majorRev >= 3 )
  1402.         {
  1403.             return ((feature & (1L << gestaltMultiChannels)) != 0);
  1404.         }
  1405.         else
  1406.         {
  1407.             err = Gestalt( gestaltHardwareAttr, &feature );
  1408.             if ( err == noErr )
  1409.                 return ((feature & (1L << gestaltHasASC)) != 0);
  1410.         }
  1411.     }
  1412.     return false;
  1413. }
  1414.  
  1415.  
  1416. /*===========================================================================*/
  1417. /*                       PRIVATE FUNCTION DEFINITIONS                        */
  1418. /*===========================================================================*/
  1419.  
  1420.  
  1421. PRIVATE OSErr
  1422. PlayMusicBlock( short channelNum )
  1423. {
  1424.     short        soundRef;
  1425.     OSErr        err;
  1426.     
  1427.     if ( snd->musicPosition < 0 )
  1428.         return noErr;
  1429.         
  1430.     soundRef = snd->musicSequence->sequence[snd->musicPosition];
  1431.     if ( soundRef < 0 )
  1432.         return noErr;
  1433.  
  1434.         /* DJH on 03/03/96: use the common routine shared by CMSPlaySound() */
  1435.     
  1436.     err = StartSound( (SndChannelPtr) snd->soundChannel[channelNum],
  1437.                       channelNum, soundRef, kMusicDone );
  1438.  
  1439.     return err;
  1440. }
  1441.         
  1442. /*---------------------------------------------------------------------------*/
  1443. /* DA on 01/20/96:     redeclared theCmd as a pointer rather than a whole
  1444. ** structure, to enable this routine to be called on PowerMacs too
  1445. */
  1446.  
  1447. PRIVATE pascal void
  1448. SoundCallBack( SndChannelPtr theChannel, SndCommand *theCmd )
  1449. {
  1450.     long    myA5;
  1451.     short    channelNum;
  1452.                         
  1453.     myA5 = SetA5( theCmd->param2 );
  1454.     
  1455.     switch( theCmd->param1 )
  1456.     {
  1457.         case kSoundDone:
  1458.         case kSoundResourceDone:
  1459.         
  1460.                 /*    A sound has finished playing on the sound channel so
  1461.                 **    mark the sound channel as no longer playing any sound
  1462.                 **    and return.
  1463.                 */
  1464.             channelNum = theChannel->userInfo;
  1465.             snd->soundChannel[channelNum]->soundPlaying = kNoSound;            
  1466.             break;
  1467.         
  1468.         
  1469.                         
  1470.         case kMusicDone:
  1471.         
  1472.                 /*    Advance the music position counter. If we have reached
  1473.                 **    the end of the music, loop back to the appropriate place
  1474.                 **    in the sequence.
  1475.                 */
  1476.             if ( (++snd->musicPosition) >= snd->musicSequence->sequenceLength )
  1477.                 snd->musicPosition = snd->musicSequence->loopStart;
  1478.                 
  1479.                 /*    Extract the channel number and play the next music
  1480.                 **    block on that channel.
  1481.                 */
  1482.             channelNum = theChannel->userInfo;    
  1483.             PlayMusicBlock( channelNum );
  1484.             break;
  1485.     }
  1486.  
  1487.     myA5 = SetA5( myA5 );
  1488.     
  1489. }
  1490.  
  1491. /*---------------------------------------------------------------------------*/
  1492. /* DA on 01/20/96:  reads the volume of a specific channel */
  1493.  
  1494. PUBLIC OSErr
  1495. CMSGetChannelVolume( short channelNum, unsigned short *sndVolume )
  1496. {
  1497.     SndChannelPtr    chan;            /* the channel to inspect */
  1498.     SndCommand        sndCmd;            /* command to issue to the channel */
  1499.     unsigned long    volume;            /* the volume of left and right channels */
  1500.     OSErr            err;            /* error code */
  1501.  
  1502.         /* get the channel to inspect */
  1503.  
  1504.     chan = (SndChannelPtr)snd->soundChannel[channelNum];
  1505.  
  1506.  
  1507.         /* fill in the command structure to get the volume of the channel */
  1508.  
  1509.     sndCmd.cmd = getVolumeCmd;
  1510.     sndCmd.param1 = 0;
  1511.     sndCmd.param2 = (long) &volume;
  1512.     
  1513.     err = SndDoImmediate(chan, &sndCmd);
  1514.     
  1515.     
  1516.         /* extract the volume.  Try the right channel first, then left */
  1517.     
  1518.     if ( err == noErr )
  1519.     {
  1520.         *sndVolume = (unsigned short)(volume & 0x0000FFFF);
  1521.         if ( *sndVolume == 0 )
  1522.             *sndVolume = (unsigned short)((volume & 0xFFFF0000) >> 16);
  1523.     }
  1524.     else
  1525.     {
  1526.         *sndVolume = 0;
  1527.     }
  1528.     
  1529.     return err;
  1530. }
  1531.  
  1532. /*---------------------------------------------------------------------------*/
  1533. /* DA on 01/20/96:  assigns selected volume to a specific channel */
  1534.  
  1535. PUBLIC OSErr
  1536. CMSSetChannelVolume( short channelNum, unsigned short volume )
  1537. {
  1538.     SndChannelPtr    chan;            /* channel to modify */
  1539.     SndCommand        sndCmd;            /* command to set the volume */
  1540.     OSErr            err;            /* error code */
  1541.  
  1542.     chan = (SndChannelPtr)snd->soundChannel[channelNum];
  1543.  
  1544.         /*    Fill in the command data.  Assign volume to both left and right
  1545.         **    channels to provide compatibility on stereo machines.
  1546.         */
  1547.     sndCmd.cmd = volumeCmd;
  1548.     sndCmd.param1 = 0;
  1549.     sndCmd.param2 = ((long)volume << 16) | (long)volume;
  1550.     
  1551.     err = SndDoImmediate(chan, &sndCmd);
  1552.  
  1553.     
  1554.     return    err;    
  1555. }
  1556.  
  1557. /*---------------------------------------------------------------------------*/
  1558. /* DA on 01/20/96: local function play a sound using a double buffered scheme*/
  1559.  
  1560. PRIVATE OSErr
  1561. DBSndPlay( SndChannelPtr chan, short channelNum, SoundHeaderPtr sndHeader )
  1562. {
  1563.     OSErr                     err;            /* error code */
  1564.     DBLocalPtr                doubleVars;        /* local double buffer info */
  1565.     SndDoubleBufferPtr        doubleBuffer;    /* one of the buffers */
  1566.     SndDoubleBufferHeader*    doubleHeader;    /* header for the double buffer */
  1567.     short                    i;                /* misc counter */
  1568.  
  1569.         /* obtain pointers to double buffer structures for faster access */
  1570.     
  1571.     doubleVars = &snd->doubleVars[channelNum];
  1572.     doubleHeader = &snd->doubleHeader[channelNum];
  1573.     
  1574.     
  1575.         /* fill our local structure used to control the double buffering */
  1576.     
  1577.     doubleVars->header = sndHeader;
  1578.     doubleVars->bytesCopied = 0;                            /* no samples copied yet */
  1579.     doubleVars->dataPtr = (Ptr)&sndHeader->sampleArea[0];    /* pointer to the first sample */
  1580.     
  1581.     
  1582.         /* sampling rate in kHz.  This works for all sound headers. */
  1583.     
  1584.     doubleHeader->dbhSampleRate = sndHeader->sampleRate;
  1585.     
  1586.     
  1587.         /* DJH on 02/06/96: fill in the double buffer header at _play_ time
  1588.         ** rather than _init_ time based on the kind of sound header passed.
  1589.         */
  1590.     switch ( sndHeader->encode )
  1591.     {
  1592.         case stdSH:        /* Standard sound header (8-bit, 1 channel, no compression) */
  1593.         
  1594.             doubleHeader->dbhNumChannels   = 1;
  1595.             doubleHeader->dbhSampleSize    = 8;
  1596.             doubleHeader->dbhCompressionID = notCompressed;
  1597.             doubleHeader->dbhPacketSize    = 0;
  1598.  
  1599.             doubleVars->bytesTotal = sndHeader->length;    // sound data length
  1600.  
  1601.             break;
  1602.             
  1603.             
  1604.         case extSH:        /* Extended sound header */
  1605.         {
  1606.             ExtSoundHeaderPtr extHeader = (ExtSoundHeaderPtr) sndHeader;
  1607.             
  1608.             doubleHeader->dbhNumChannels   = extHeader->numChannels;
  1609.             doubleHeader->dbhSampleSize    = extHeader->sampleSize;
  1610.             doubleHeader->dbhCompressionID = notCompressed;
  1611.             doubleHeader->dbhPacketSize    = 0;
  1612.             
  1613.             doubleVars->bytesTotal =
  1614.                 extHeader->numFrames * extHeader->sampleSize * extHeader->numChannels / 8;
  1615.                 
  1616.             break;
  1617.         }
  1618.         
  1619.         
  1620.         case cmpSH:        /* Compressed sound header */
  1621.         {
  1622.             CmpSoundHeaderPtr cmpHeader = (CmpSoundHeaderPtr) sndHeader;
  1623.             
  1624.             doubleHeader->dbhNumChannels   = cmpHeader->numChannels;
  1625.             doubleHeader->dbhSampleSize    = cmpHeader->sampleSize;
  1626.             doubleHeader->dbhCompressionID = cmpHeader->compressionID;
  1627.             doubleHeader->dbhPacketSize    = cmpHeader->packetSize;
  1628.             break;
  1629.         }
  1630.     }
  1631.     
  1632.  
  1633.         /* initialize both buffers */
  1634.     
  1635.     for ( i = 0; i < 2; i++ )
  1636.     {
  1637.         doubleBuffer = snd->doubleBuffer[(channelNum * 2) + i];
  1638.  
  1639.  
  1640.         doubleBuffer->dbNumFrames = 0;                        // no frames yet
  1641.         doubleBuffer->dbFlags = 0;                            // buffer is empty
  1642.         doubleBuffer->dbUserInfo[0] = (long)doubleVars;        // pointer to our vars
  1643.  
  1644.             /*  Fill buffer with samples.  It is _necessary_ to call the doubleback
  1645.             **  function this way, otherwise it would crash on PPCs
  1646.             */
  1647.         
  1648.         CallSndDoubleBackProc( doubleBackUPP, chan, doubleBuffer );
  1649.         
  1650.     }
  1651.  
  1652.  
  1653.         /* start the sound playing */
  1654.     
  1655.     err = SndPlayDoubleBuffer( chan, doubleHeader );
  1656.     
  1657.     
  1658.     return    err;
  1659. }
  1660.  
  1661. /*---------------------------------------------------------------------------*/
  1662. /* DA on 01/20/96: local function double buffering callback procedure used to
  1663. ** feed the Sound Manager with sound data
  1664. */
  1665.  
  1666. PRIVATE pascal void
  1667. DADoubleBack( SndChannelPtr, SndDoubleBufferPtr doubleBuffer )
  1668. {
  1669.     DBLocalPtr     doubleVarsPtr;
  1670.     long         bytesToCopy;
  1671.  
  1672.  
  1673.         /* get pointer to our local variables */
  1674.     
  1675.     doubleVarsPtr = (DBLocalPtr)doubleBuffer->dbUserInfo[0];
  1676.  
  1677.  
  1678.  
  1679.         /* get number of bytes left to copy */
  1680.     
  1681.     bytesToCopy = doubleVarsPtr->bytesTotal - doubleVarsPtr->bytesCopied;
  1682.  
  1683.  
  1684.  
  1685.         /* If the amount left is greater than double-buffer size, then
  1686.         ** limit the number of bytes to copy to the size of the buffer.
  1687.         */
  1688.     if ( bytesToCopy > kDoubleBufferSize )
  1689.     {
  1690.         bytesToCopy = kDoubleBufferSize;
  1691.     }
  1692.  
  1693.  
  1694.  
  1695.         /* copy samples to double buffer
  1696.         ** uses BlockMoveData to avoid flushing the cache on 040 machines
  1697.         */
  1698.     BlockMoveData(doubleVarsPtr->dataPtr, &doubleBuffer->dbSoundData[0], bytesToCopy);
  1699.  
  1700.  
  1701.         /* store the number of samples in buffer */
  1702.     
  1703.     switch ( doubleVarsPtr->header->encode )
  1704.     {
  1705.         case stdSH:        /* Standard sound header */
  1706.         
  1707.             doubleBuffer->dbNumFrames = bytesToCopy;
  1708.             break;
  1709.             
  1710.             
  1711.         case extSH:        /* Extended sound header */
  1712.         {
  1713.             ExtSoundHeaderPtr extHeader = (ExtSoundHeaderPtr) doubleVarsPtr->header;
  1714.             
  1715.             doubleBuffer->dbNumFrames =
  1716.                 (bytesToCopy * 8) / (extHeader->numChannels * extHeader->sampleSize);
  1717.                 
  1718.             break;    
  1719.         }
  1720.         
  1721.         
  1722.         case cmpSH:        /* Compressed sound header */
  1723.         {
  1724.                 /* Do I need to decompress the data myself? */
  1725.                 
  1726.             CmpSoundHeaderPtr cmpHeader = (CmpSoundHeaderPtr) doubleVarsPtr->header;
  1727.             
  1728.             doubleBuffer->dbNumFrames = 
  1729.                 (bytesToCopy * 8) / (cmpHeader->numChannels * cmpHeader->sampleSize);
  1730.             
  1731.             break;
  1732.         }
  1733.         
  1734.         
  1735.         default:
  1736.             break;
  1737.     }
  1738.     
  1739.     
  1740.     
  1741.         /* Mark the buffer as ready */
  1742.         
  1743.     doubleBuffer->dbFlags |= dbBufferReady;
  1744.  
  1745.  
  1746.  
  1747.         /* Update data pointer and number of bytes copied */
  1748.     
  1749.     doubleVarsPtr->dataPtr += bytesToCopy;
  1750.     doubleVarsPtr->bytesCopied += bytesToCopy;
  1751.  
  1752.  
  1753.  
  1754.         /* If all samples have been copied, then this is the last buffer. */
  1755.     
  1756.     if ( doubleVarsPtr->bytesCopied == doubleVarsPtr->bytesTotal )
  1757.     {
  1758.         doubleBuffer->dbFlags |= dbLastBuffer;
  1759.     }
  1760. }
  1761.  
  1762. /*---------------------------------------------------------------------------*/
  1763. /* DA on 01/20/96:  play a sound with specified priority */
  1764.  
  1765. PUBLIC OSErr
  1766. CMSPlaySoundPriority( short sound, short priority )
  1767. {
  1768.     short    i;                /* misc index */
  1769.     short    free = -1;        /* index of a channel that is free */
  1770.     short    sndRef;            /* sound currently playing */
  1771.     short    minPriority;    /* the minimum priority on all channels */
  1772.     short    minPTrack;        /* channel that has the minimum priority */
  1773.     short    tracksUsed;        /* number of channels currently open */
  1774.     OSErr    err = noErr;    /* error code */
  1775.     
  1776.     tracksUsed = snd->numChannels;        /* number of open channels */
  1777.     
  1778.     
  1779.         /*  First, rebuild priorites list and search for a free channel  */
  1780.     
  1781.     for (i = 0; i < tracksUsed; i++)
  1782.     {
  1783.         err = CMSGetSoundPlaying( i, &sndRef );
  1784.         
  1785.         if ( sndRef == kNoSound )
  1786.         {    
  1787.             snd->priorities[i] = 0;
  1788.             free = i;
  1789.             break;
  1790.         }
  1791.  
  1792.     }
  1793.     
  1794.     
  1795.     
  1796.         /*  If there's a free channel, use that one, otherwise search
  1797.         **  for the channel with the lowest priority and see if we can
  1798.         **  play the sound on that one
  1799.         */
  1800.     if ( free >= 0 )
  1801.     {    
  1802.         err = CMSPlaySound( sound, free );
  1803.         snd->priorities[free] = priority;
  1804.     }
  1805.     else
  1806.     {
  1807.             /* search for the lowest priority of any sound playing */
  1808.     
  1809.         for ( i = minPTrack = 0, minPriority = snd->priorities[0]; i < tracksUsed; i++ )
  1810.         {
  1811.             if ( snd->priorities[i] < minPriority )
  1812.             {
  1813.                 minPriority = snd->priorities[i];
  1814.                 minPTrack = i;
  1815.             }
  1816.         }
  1817.             
  1818.         if ( minPriority < priority )    /* ok to play the sound */
  1819.         {
  1820.             err = CMSPlaySound( sound, minPTrack );
  1821.             
  1822.             
  1823.                 /* NOTE: We MUST set the priority after we play the sound since
  1824.                 ** CMSPlaySound gives the sound a priority of 0 by default.
  1825.                 */
  1826.             snd->priorities[minPTrack] = priority;
  1827.             
  1828.             if ( err == noErr )
  1829.                 err = kPriorityOverride;    /* a lower priority was found */
  1830.         }
  1831.         else
  1832.         {
  1833.             err = kPriorityDenied;    /* no lower priority found: sound not played */
  1834.         }
  1835.     }
  1836.     
  1837.     return    err;
  1838. }
  1839.  
  1840. /*---------------------------------------------------------------------------*/
  1841. /* DA on 01/20/96:  change the volume for all open channels */
  1842.  
  1843. PUBLIC OSErr
  1844. CMSSetToolVolume( short volume )
  1845. {
  1846.     short            channel, tracksUsed;
  1847.     unsigned short    setting;
  1848.     OSErr            err;
  1849.     
  1850.         /* number of open channels */
  1851.     
  1852.     tracksUsed = snd->numChannels;
  1853.     
  1854.     
  1855.         /* operate the change on all channels */
  1856.     
  1857.     for ( channel = 0; channel < tracksUsed; channel++ )
  1858.     {
  1859.             /* read current volume */
  1860.  
  1861.         err = CMSGetChannelVolume( channel, &setting );
  1862.         if ( err != noErr )
  1863.             return    err;
  1864.  
  1865.  
  1866.         
  1867.             /* change it according to specified parameter */
  1868.         
  1869.         setting += volume;
  1870.         
  1871.         if ( volume > 0 )
  1872.         {
  1873.             if ( setting > kMaxVolume )
  1874.                 setting = kMaxVolume;
  1875.         }
  1876.         else if ( volume < 0 )
  1877.         {
  1878.             if ( ((short)setting) < kMinVolume )
  1879.                 setting = kMinVolume;
  1880.         }
  1881.         
  1882.         err = CMSSetChannelVolume( channel, setting );
  1883.         if ( err != noErr )
  1884.             return    err;
  1885.     }
  1886.     
  1887.     return    noErr;
  1888. }
  1889.